package esercitazione1;

import java.util.Scanner;

public class Client1_2 {
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		MyStack<Integer> values = new MyStack<Integer>();
		MyStack<Character> operations = new MyStack<Character>();
		char[] inputChar = null;
		String expression = "";

		while(true){
			// LEGGE L'ESPRESSIONE
			System.err.flush();
			System.out.println("Inserisci un'espressione di interi da valutare (digita 'quit' per uscire):");
			expression = s.nextLine();
			if(expression.equals("quit"))
				break;
			inputChar = expression.toCharArray();

			// CARICA L'ESPRESSIONE NEI DUE STACK E LA VALUTA
			int result;
			try {
				result = evalExpression(inputChar, values, operations);
				System.out.println("Il risultato è: " + result);
				System.out.println();
				
				// Re-inizializza gli stack se necessario
				svuotaStack(values, operations);
				
			} catch (UnsupportedOperationException e) {
				System.err.println("ERRORE!! L'espressione inserita genera operazioni illecite.");
				svuotaStack(values, operations);
			} catch (IllegalArgumentException e) {
				System.err.println("ERRORE!! L'espressione inserita non è scritta in modo corretto.");
				svuotaStack(values, operations);
			}			
		}
		s.close();
	}

	private static void svuotaStack(MyStack<Integer> values, MyStack<Character> operations) {
		
		while (!operations.isEmpty())
			operations.pop();

		while (!values.isEmpty())
			values.pop();
	}

	// Valuta l'espressioen aritmetica inserita
	public static int evalExpression(char[] inputChar, MyStack<Integer> values,
			MyStack<Character> operations) {
		for (int i = 0; i < inputChar.length; i++) {

			// Se il carattere è uno spazio, lo salta.
			if (inputChar[i] == ' ')
				continue;

			// Se il carattere è un numero, lo inserisce nello stack dei numeri.
			if (inputChar[i] >= '0' && inputChar[i] <= '9') {
				StringBuffer longNumber = new StringBuffer();
				int k = i;
				while (k < inputChar.length && inputChar[k] >= '0' && inputChar[k] <= '9')
					longNumber.append(inputChar[k++]);
				values.push(Integer.parseInt(longNumber.toString()));
				
				if(k-i >= 2)
					i += k-i-1;
			}
			
			// Se il carattere è l'apertura di una parentesi, lo inserisce nello
			// stack delle operazioni.
			else if (inputChar[i] == '(')
				operations.push(inputChar[i]);

			// Se il carattere è la chiusura di una parentesi, valuta
			// l'espressione all'interno della parentesi.
			else if (inputChar[i] == ')') {
				if(values.size() >= 2 && !operations.isEmpty())
					while (operations.top() != '(')
						values.push(evalBracket(operations.pop(), values.pop(), values.pop()));
				else
					throw new IllegalArgumentException();
				operations.pop();
			}

			// Se il carattere è un'operazione consentita, valuta la sua precedenza rispetto a quelle nello stack operazioni.
			else if (inputChar[i] == '+' || inputChar[i] == '-' || inputChar[i] == '*' || inputChar[i] == '/') {
				while (!operations.isEmpty() && (operations.top().equals('*') || operations.top().equals('/') || operations.top().equals('-')))
					if(values.size() >= 2)
						values.push(evalBracket(operations.pop(), values.pop(), values.pop()));
					else
						throw new IllegalArgumentException();

				operations.push(inputChar[i]);
			}
			
			else
				throw new IllegalArgumentException();
		}

		// Se rimangono dei caratteri all'interno dello stack, li valuto.
		if(!operations.isEmpty()){
			while (!operations.isEmpty())
				if(values.size() >= 2)
					values.push(evalBracket(operations.pop(), values.pop(), values.pop()));
				else
					throw new IllegalArgumentException();
		}
		
		// L'ultimo valore nello stack dei numeri contiene il risultato.
		return values.pop();
		
	}

	// Valuta la singola espressione aritmetica all'interno di una parentesi
	public static int evalBracket(char op, int val2, int val1) {
		System.out.println(val1+" "+op+" "+val2);
		switch (op) {
		case '+':
			return val1 + val2;
		case '-':
			return val1 - val2;
		case '*':
			return val1 * val2;
		case '/':
			if (val2 == 0)
				throw new UnsupportedOperationException();
			return val1 / val2;
		}
		return 0;
	}
}
